import { DrawLoadData, DrawReadData, DrawReadSaveData, DrawUtilsI } from '../type';
import WatchBase from '@/utils/WatchValue';
import Visual from '@/visual/Visual';

/** 数据控制工具类 */
export default class DataUtils implements DrawUtilsI {

	/** 可视化控制类 */
	private visual: Visual;

	/** 加载过程中的数据 */
	public drawLoadData!: DrawLoadData;

	/** 读取过程中的数据 */
	public drawReadData!: DrawReadData;

	/** 需要保存的读取过程数据 */
	public drawOldData!: DrawReadSaveData | null;

	/** 保存的数据 */
	private saveValueRowList: number[][] = [];

	/**
	 * 构造函数
	 * @param visual 可视化类
	 */
	public constructor(visual: Visual) {
		this.visual = visual;
	}

	/** @override */
	public async Load() {
		this.Reset();
		await this.LoadBackgroundImage();
		await this.LoadCSV();
		await this.MakeIconList();
		this.HandleData();
	}

	/** 重置数据 */
	private Reset() {
		this.drawLoadData = {
			valueRowList: new WatchBase<number[][]>([]),
			timeList: [],
			rowList: [],
			iconList: [],
			backgroundImage: new WatchBase<CanvasImageSource | undefined>(undefined),
		};
		this.drawReadData = {
			nowDate: new WatchBase<string>(""),
			saveData: {
				minValue: 0,
				maxValue: 0,
				valueList: [],
				sortValueList: [],
			},
			sortIndexList: [],
			nowIcon: new WatchBase<CanvasImageSource>(new Image()),
		};
		this.drawOldData = null;
	}

	/** 加载背景图片 */
	private async LoadBackgroundImage() {
		const { visual, drawLoadData } = this;
		const { configUtils, } = visual;
		drawLoadData.backgroundImage = await configUtils.LoadBackgroundImage();
	}

	/** 加载CSV表格数据文件 */
	private async LoadCSV() {
		const { visual, drawLoadData } = this;
		const { configUtils } = visual;
		const csvString = await configUtils.LoadCSV();
		const csvData = csvString.trim().split(/\n|\r\n/).map(rowString => rowString.split(","));
		const index = csvData.findIndex(row => row[0].includes("\\"));
		const timeRowData = csvData.slice(index + 1);
		const infoRowData = csvData.slice(0, index + 1);
		drawLoadData.timeList = timeRowData.map(row => row[0]);
		this.saveValueRowList = timeRowData.map(row => row.slice(1).map(value => parseInt(value)));
		this.MakeInfoRowList(infoRowData);
	}

	/** 计算CSV文件附件数据 */
	private MakeInfoRowList(infoRowData: string[][]) {
		const { drawLoadData } = this;
		const length = infoRowData.length;
		drawLoadData.rowList = new Array(infoRowData[0].length - 1).fill(null).map((_, j) => {
			const info: { [key: string]: string } = {};
			for (let i = 0; i < length; i++) {
				const data = infoRowData[i];
				const key: string = i !== length - 1 ? data[0] : data[0].split("\\")[1];
				const infoRow = data.slice(1);
				const row = infoRow[j];
				info[key] = row;
			}
			return info;
		});
	}

	/** 创建图标数组 */
	private async MakeIconList() {
		const { visual, drawLoadData } = this;
		const { configUtils } = visual;
		const { rowList } = drawLoadData;
		const iconList = drawLoadData.iconList;
		for (const info of rowList) {
			const icon = await configUtils.getInfoIcon(info);
			iconList.push(icon);
		}
	}

	/** 处理数据 */
	private HandleData(): void {
		const { visual, drawLoadData } = this;
		const { configUtils } = visual;
		const { sortDate, dataStack } = configUtils.config;
		drawLoadData.valueRowList = new WatchBase<number[][]>(() => {
			const saveValueRowList = [...this.saveValueRowList.map(list => [...list])];
			const sortRowList = this.SortValueByDate(saveValueRowList);
			this.StackData(sortRowList);
			return saveValueRowList;
		}, sortDate, dataStack);
	}

	/** 按日期排序数值 */
	private SortValueByDate(saveValueRowList: number[][]): number[][] {
		const { visual, drawLoadData } = this;
		const { configUtils } = visual;
		const { sortDate } = configUtils.config;
		const { timeList } = drawLoadData;
		if (!sortDate) { return saveValueRowList; }
		const timeIndexList = timeList.map((date, index) => ({ date, index }));
		const sortTimeIndexList = timeIndexList.sort((x, y) => new Date(x.date).getTime() - new Date(y.date).getTime());
		const sortTimeList = sortTimeIndexList.map(info => info.date);
		const sortValueRowList = sortTimeIndexList.map(info => saveValueRowList[info.index]);
		drawLoadData.timeList = sortTimeList;
		return sortValueRowList;
	}

	/** 叠加数值 */
	private StackData(sortRowList: number[][]) {
		const { visual, drawLoadData } = this;
		const { configUtils } = visual;
		const { dataStack } = configUtils.config;
		const { rowList } = drawLoadData;
		if (!dataStack.Value) { return; }
		const saveList = rowList.map(() => 0);
		sortRowList.forEach((list) => {
			list.forEach((value, i) => {
				list[i] = saveList[i] += value;
			});
		});
	}

	/** @override */
	public LoadingData() {
		this.ReadInfoList();
		this.CalcNowDate();
	}

	/** 读取数据条数据 */
	private ReadInfoList() {
		const { visual } = this;
		const { dataUtils, positionUtils, configUtils } = visual;
		const { drawLoadData, drawReadData } = dataUtils;
		const { DateReverse, ValueReverse, firstIndx, maxLength, minRate } = configUtils.config;
		const { valueRowList: { Value: valueRowList } } = drawLoadData;
		const { saveData } = drawReadData;
		const oldSpeed = positionUtils.Speed;
		if (oldSpeed === 0) {
			dataUtils.drawOldData = null;
		} else {
			dataUtils.drawOldData = saveData;
		}
		positionUtils.NextSpeed();
		const speed = positionUtils.Speed;
		const length = valueRowList.length;
		const index = !DateReverse.Value ? speed : length - 1 - speed;
		const valueList = valueRowList[index];
		const valueInfoData = valueList.map((value, index) => ({ value, index }));
		const valueUp = ValueReverse.Value ? -1 : 1;
		const sortList = valueInfoData.sort((x, y) => y.value * valueUp - x.value * valueUp);
		const sortValueList = sortList.map(({ value }) => value);
		const showList = sortValueList.slice(firstIndx.Value, firstIndx.Value + maxLength.Value);
		const maxValue = Math.max(...showList);
		let minValue = Math.min(...showList);
		const minRateV = minRate.Value;
		if (minRateV) {
			minValue = maxValue - (maxValue - minValue) * minRateV;
		} else {
			minValue = 0;
		}
		drawReadData.saveData = { valueList, maxValue, minValue, sortValueList };
		drawReadData.sortIndexList = sortList.map(({ index }) => index);
	}

	/** 计算当前日期 */
	private CalcNowDate() {
		const { visual } = this;
		const { dataUtils, positionUtils, configUtils } = visual;
		const { drawLoadData, drawReadData } = dataUtils;
		const { DateReverse } = configUtils.config;
		const { timeList } = drawLoadData;
		const { Speed } = positionUtils;
		const length = timeList.length;
		const index = !DateReverse.Value ? Speed : length - 1 - Speed;
		drawReadData.nowDate.Value = timeList[index];
	}

	/** @override */
	public BeforeDraw() { }

	/** @override */
	public Draw() { }
}
